package com.yupi.springbootinit.BiMQ.BiMq;

import cn.hutool.json.JSONUtil;
import com.rabbitmq.client.Channel;
import com.yupi.springbootinit.GuaVa.RetryManager;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.component.WebSocketServer;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.exception.ThrowUtils;
import com.yupi.springbootinit.manager.AIManager;
import com.yupi.springbootinit.model.entity.CallAndGenerateCounts;
import com.yupi.springbootinit.model.entity.ChartInfo;
import com.yupi.springbootinit.service.CallAndGenerateCountsService;
import com.yupi.springbootinit.service.ChartInfoService;
import com.yupi.springbootinit.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class BiMessageConsumer {
    @Resource
    private ChartInfoService chartInfoService;

    @Resource
    private AIManager aiManager;

    @Resource
    private RetryManager retryManager;

    private String result = null;

    @Resource
    private RedisUtils redisUtils;

    @Resource
    private CallAndGenerateCountsService countsService;



    @Resource
    private WebSocketServer webSocketServer;
    private static Long ModelId = 1659171950288818178L;
    //todo 后期优化，将服务抽离这里只关心消费问题，而不关心待完成任务，与发送消息
    @RabbitListener(queues = {BIMQConstant.BI_QUEUE_NAME},ackMode = "MANUAL")
    public void receive(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {
        log.info("消息传递的参数为：{}",message);
        if (StringUtils.isBlank(message)){
            channel.basicNack(deliveryTag,false,false);
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"消息不能为空");
        }
        String[] split = message.split(",");
        Long chatId = Long.valueOf(split[0]);
        Long userID = Long.valueOf(split[1]);
        Long countsID = Long.valueOf(split[2]);

        ChartInfo chartInfo = chartInfoService.getById(chatId);
        if (chartInfo==null){
            channel.basicNack(deliveryTag,false,false);
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"图表信息不能为空");
        }
        String goal = chartInfo.getGoal();
        // 构造用户输入
        StringBuilder userInput = new StringBuilder();
        userInput.append("分析需求：").append("\n");
        String chartType = chartInfo.getCharType();
        String data = chartInfo.getCharData();
        // 拼接分析目标
        String userGoal = goal;
        // 如果图表类型不为空
        if (StringUtils.isNotBlank(chartType)) {
            // 就将分析目标拼接上“请使用”+图表类型
            userGoal += "，请使用" + chartType;
        }
        userInput.append(userGoal).append("\n");
        userInput.append("原始数据：").append("\n");
        userInput.append(data).append("\n");

        // 先修改数据库中的数据的状态改为running，
        ChartInfo info = new ChartInfo();
        info.setId(chartInfo.getId());
        info.setStatus("running");
        //如果修改失败就意味着数据库出现问题了，如果修改失败了，也要向数据库中存储信息，将status状态改为failed,这里直接抽象成一个
        boolean updateResult = chartInfoService.updateById(info);
        // 公用的方法
        if (!updateResult){
            channel.basicNack(deliveryTag,false,false);
            handleUpdateError(chartInfo.getId(),"图表信息不能为Null",userID);
            return;
        }
        Callable<String> callable =()->{
            //处理业务逻辑
            result = aiManager.doChart(ModelId, userInput.toString());
          return  result;
        };
        try {
            //调用失败重试3次
            retryManager.RetryManager().call(callable);
        } catch (Exception e) {
            log.info("调用AI失败原因：message{}",e.getCause().getMessage());
            //todo 失败就放入死信队列
            channel.basicNack(deliveryTag,false,false);
            handleUpdateError(chartInfo.getId(),e.getCause().getMessage(),userID);
            //todo 此时更新图表失败，放入缓存中，并且设置有效时间为1个小时
            //利用 String数据结构存储，key为chartId 值是对象;
            String jsonStr = JSONUtil.toJsonStr(chartInfo);
            Long chartId = chartInfo.getId();
            redisUtils.setStringKey(chartId.toString(),jsonStr);
            redisUtils.stringExpire(chartId.toString(),1, TimeUnit.HOURS);
        }

        String[] splits = result.split("【【【【【");
        // 拆分之后还要进行校验
        if (splits.length < 3) {
            channel.basicNack(deliveryTag,false,false);
            handleUpdateError(chartInfo.getId(),"AI生成图表错误",userID);
            return;
        }
        //拆分出来后的内容
        String chartCode = splits[1].trim();
        String chartResult = splits[2].trim();
        //当AI生成成功以后继续更新数据库图表的内容并且，更新状态为成功
        ChartInfo updateChartInfo = new ChartInfo();
        updateChartInfo.setId(chartInfo.getId());
        updateChartInfo.setStatus("succeed");
        updateChartInfo.setGenChart(chartCode);
        updateChartInfo.setGenResult(chartResult);
        boolean updateResult2 = chartInfoService.updateById(updateChartInfo);
        if (!updateResult2){
            channel.basicNack(deliveryTag,false,false);
            handleUpdateError(chartInfo.getId(),"更新图表状态失败",userID);
        }
        log.info("发送的消息为message===>:{}",message);
        try {
            //todo 失败放到死信队列
            channel.basicAck(deliveryTag,false);
        } catch (IOException e) {
            channel.basicNack(deliveryTag,false,false);
            log.info("messageThrow{}",e.getMessage());
            throw new RuntimeException(e);
        }
        try {
            CallAndGenerateCounts counts = countsService.getById(countsID);
            Integer remainTasks = counts.getRemainTasks();
            remainTasks = remainTasks-1;
            counts.setRemainTasks(remainTasks);
            redisUtils.delCacheMapValue(split[1],split[1]);
            countsService.updateById(counts);
            //todo 成功要减一条待完成任务
           webSocketServer.sendInfo(updateChartInfo.getStatus(),userID);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void handleUpdateError(Long chartId, String execMessage,Long userId) {
        ChartInfo updateChartInfo = new ChartInfo();
        updateChartInfo.setId(chartId);
        updateChartInfo.setStatus("failed");
        updateChartInfo.setExecMessage(execMessage);
        boolean b = chartInfoService.updateById(updateChartInfo);
        ThrowUtils.throwIf(!b,new BusinessException(ErrorCode.SYSTEM_ERROR,"更新图表失败状态失败"));
        //todo 将webSocket拆分成单个的服务,用来发送消息
        try {
            webSocketServer.sendInfo(updateChartInfo.getStatus(),userId);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}





